package com.mini.mybatis.mybatisministep12.builder.xml;

import com.mini.mybatis.mybatisministep12.builder.BaseBuilder;
import com.mini.mybatis.mybatisministep12.datasource.DataSourceFactory;
import com.mini.mybatis.mybatisministep12.io.Resources;
import com.mini.mybatis.mybatisministep12.mapping.Environment;
import com.mini.mybatis.mybatisministep12.sqlsession.configuration.Configuration;
import com.mini.mybatis.mybatisministep12.transaction.TransactionFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.xml.sax.InputSource;

import javax.sql.DataSource;
import java.io.InputStream;
import java.io.Reader;
import java.util.*;

public class XmlConfigBuilder extends BaseBuilder {

    private Element rootElement;

    public XmlConfigBuilder(Reader reader) {

        // 调用父类初始化configuration
        super(new Configuration());

        // 利用dom4j解析xml
        SAXReader saxReader = new SAXReader();

        try {
            Document document = saxReader.read(new InputSource(reader));
            rootElement = document.getRootElement();
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 解析mybatis全局配置文件,主要解析mappers
     * @return
     */
    public Configuration parse(){

        try {
            // 解析environments
            environmentsElement(rootElement.element("environments"));

            // 解析mappers
            mappersElement(rootElement.element("mappers"));

        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return configuration;
    }

    private void environmentsElement(Element environments) throws Exception {
        // 解析environments标签
        String environmentId = environments.attributeValue("default");
        List<Element> elementList = environments.elements("environment");
        for (Element e : elementList) {
            // 每个environment标签的id
            String id = e.attributeValue("id");
            if (environmentId.equals(id)){
                // 事务管理器 通过配置类中的type类型，获取已经在Configuration类中初始化时保存好的事务管理器，下面的DataSourceFactory也一样
                TransactionFactory txFactory = (TransactionFactory) typeAliasRegistry.resolveAlias(e.element("transactionManager").attributeValue("type")).newInstance();
                // 数据源
                Element datasourceElement = e.element("dataSource");
                DataSourceFactory dataSourceFactory = (DataSourceFactory) typeAliasRegistry.resolveAlias(datasourceElement.attributeValue("type")).newInstance();
                List<Element> propertyList = datasourceElement.elements("property");
                Properties props = new Properties();
                for (Element property : propertyList) {
                    props.setProperty(property.attributeValue("name"),property.attributeValue("value"));
                }
                dataSourceFactory.setProperties(props);
                DataSource dataSource = dataSourceFactory.getDataSource();

                Environment.Builder environmentBuilder = new Environment.Builder(id)
                        .transactionFactory(txFactory)
                        .dataSource(dataSource);

                configuration.setEnvironment(environmentBuilder.build());
            }
        }
    }

    /**
     * <mappers>
     *      <mapper resource="mapper/UserMapper.xml"/>
     *      <mapper resource="mapper/PetMapper.xml"/>
     *  </mappers>
     *
     * 读取每个mapper.xml并解析，
     * @param mappers
     * @throws Exception
     */
    private void mappersElement(Element mappers) throws Exception{

        List<Element> mapperList = mappers.elements("mapper");

        for (Element e : mapperList) {
            String resource = e.attributeValue("resource");
            String mapperClass = e.attributeValue("class");
            if (resource != null && mapperClass == null){
                // XML解析
                InputStream inputStream = Resources.getResourceAsStream(resource);
                // 解析mapper文件的操作，直接交给XmlMapperBuilder，分工明确
                /**
                 *  理解这种编码思想，我们用一个类去专门执行某种功能的时候，我们不需要把我们需要的参数传到每个具体方法里，
                 *  我们只需要定义属性，比如通过new 这个类对象的时候通过构造函数初始化这些属性，之后通过这个对象的引用调
                 *  用的方法都可以使用到这些属性了。比如下面的parse()方法。
                 */
                XmlMapperBuilder xmlMapperBuilder = new XmlMapperBuilder(inputStream, configuration, resource);
                xmlMapperBuilder.parse();
            }else if (resource == null && mapperClass != null){
                // Annotation注解解析
                Class<?> mapperInterface = Resources.classForName(mapperClass);
                configuration.addMapper(mapperInterface);
            }

        }
    }
}
